home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-03-15 | 13.4 KB | 387 lines | [TEXT/KAHL] |
- /*
- GDFrameRate.c
-
- (For background, read "Video synch" on the VideoToolbox disk, and run TimeVideo,
- which reports the results of running these routines.)
-
- double GDFrameRate(GDHandle device) measures the frame rate of a video device in
- Hz. (A NULL "device" argument to this routine, or any of the routines below,
- requests use of the System VBL interrupt, which normally runs at 60.15 Hz.) It
- times by counting VBL interrupts (discarding any spurious ones, to deal with
- the problem described in the next paragraph).
-
- double GDVBLRate(GDHandle device) measures the rate of VBL interrupts generated
- by a video device in Hz. According to Apple's Designing Cards and Drivers book
- and other documentation, the video driver and card are supposed to generate one
- VBL interrupt per frame. However, many don't. E.g. Apple's 4•8 and 8•24 video
- cards issue several interrupts per frame. Read the "Video synch" file.
-
- double GDMovieSize(GDHandle device,int quickly) measures what fraction of the
- screen you can fill with a real-time movie (a new image on each frame), using
- CopyBitsQuickly (if quickly!=0) or CopyBits (if quickly==0) to copy from memory
- to video card. At one time CopyBitsQuickly() was much faster than CopyBits(),
- but my latest measurements, using GDMovieRate, indicate that there is no longer
- any difference in speed. However, CopyBitsQuickly ignores the color tables and
- CopyBits uses them. Of course, when you're showing movies you don't want to
- waste time with color tables, so in GDMovieRate() I made a PixMap that shared
- the device's color table. For reasons that I don't understand even in that case
- using CopyBits to copy from and then back to the screen doesn't always preserve
- the original colors.
-
- double GDMovieRate(GDHandle device,int quickly) measures the rate (images/s) at
- which you can show a full-screen movie.
-
- GDFramesPerClutUpdate is obsolete. Use GDTimeClut.c instead.
-
- GDClutUpdateRate is obsolete. Use GDTimeClut.c instead.
-
- GDTimeClutUpdate is obsolete. Use GDTimeClut.c instead.
-
- HISTORY:
- 8/22/92 dgp wrote 'em.
- 8/26/92 dgp added clutEntries argument
- 8/28/92 dgp updated to use new reentrant Timer.c
- 9/11/92 dgp enhanced GDFrameRate() to discard bogus VBL interrupts and to return
- true frame rate, as suggested by Raynald Comtois. Added GDVBLRate(),
- which corresponds to the old GDFrameRate().
- 9/15/92 dgp GDMovieRate() now asks the video driver what mode we're in, just in case
- QuickDraw's been fooled.
- 9/17/92 dgp Added second argument to GDMovieRate() to select CopyBitsQuickly vs CopyBits.
- 10/5/92 dgp fixed bug in GDMovieRate() that caused crashes or noop when testing other
- than the main device.
- 10/9/92 dgp now actually initialize the linearTable for direct clut.
- 10/10/92 dgp Squeezed out extra space from rowBytes in GDMovieRate() so as not to show
- garbage. Now use Temporary memory if there isn't enough memory in the application
- heap to show a full-screen movie. Show movie for 3 seconds.
- 10/13/92 dgp In response to a report from Tom Busey, that frames were going uncounted
- during the clut timing, which seems to be a problem with some video drivers,
- GDFramesPerClutUpdate() now double checks the timing in secs, and if
- it finds a discrepancy, prints a warning to the screen and reports a
- corrected values based on the timing in secs. This should be more reliable.
- 10/13/92 dgp Fixed error in printf in GDFrameRate().
- 11/23/92 dgp Set nominalBits equal to pixelSize instead of Log2L(ctsize).
- 12/30/92 dgp Commented out warning from GDFramesPerClutUpdate().
- •Check for SetEntries error in the clut timing routines, and return
- NAN in that case. •Use trial and error to determine clut size.
- 1/4/92 dgp GDTimeClutUpdate now returns NAN on GDSetEntries error.
- 1/6/92 dgp Fixed computation of linearTable, so CLUT is preserved by GDTimeClutUpdate.
- 1/11/93 dgp Enhanced GDMovieRate() to work even when Color QuickDraw is absent, by
- calling the new routine GDMovieRateNoColorQuickDraw().
- Check returned Ptr from NewTimer() to make sure it's ok; will be NULL
- if computer only has Standard Time Manager.
- 1/24/93 dgp Reduced timing interval from 3 to 1 s for movies.
- 3/15/93 dgp Fixed portRect clipping error in GDMovieRateNoColorQuickDraw.
- */
- #include "VideoToolbox.h"
- #include <assert.h>
- #include <math.h>
- #if THINK_C
- #include <LoMem.h>
- #else
- short MBarHeight : 0xBAA;
- #endif
- double GDMovieRateNoColorQuickDraw(int quickly);
- #define SHOW_MOVIE_WINDOW 0 // A matter of taste, but I prefer not to show it.
- #define FRAMES 10 // fewer for speed, more for accuracy
-
- double GDFrameRate(GDHandle device)
- {
- VBLTaskAndA5 vblData;
- Timer *timer;
- register long frames;
- int error;
- double s;
-
- timer=NewTimer();
- if(timer==NULL)return NAN; // lacks Revised Time Manager.
- vblData.subroutine=NULL;
- error=VBLInstall(&vblData,device,FRAMES);
- if(error)PrintfExit("GDFrameRate: VBLInstall: error %d\n",error);
- vblData.vbl.vblCount=1; // Enable interrupt service routine
- frames=vblData.framesDesired-2;
- while(vblData.framesLeft>frames); // wait for second frame
- StartTimer(timer);
- while(vblData.framesLeft); // wait for last frame
- s=StopTimerSecs(timer);
- VBLRemove(&vblData);
- DisposeTimer(timer);
- return frames/s;
- }
-
- double GDVBLRate(GDHandle device)
- {
- VBLTaskAndA5 vblData;
- Timer *timer;
- register long frames;
- int error;
- double s;
-
- timer=NewTimer();
- if(timer==NULL)return NAN; // lacks Revised Time Manager.
- vblData.subroutine=SimpleVBLSubroutine;
- error=VBLInstall(&vblData,device,FRAMES);
- if(error)PrintfExit("GDVBLRate: VBLInstall: error %d\n",error);
- vblData.vbl.vblCount=1; // Enable interrupt service routine
- frames=vblData.framesDesired-2;
- while(vblData.framesLeft>frames); // wait for second frame
- StartTimer(timer);
- while(vblData.framesLeft); // wait for last frame
- s=StopTimerSecs(timer);
- VBLRemove(&vblData);
- DisposeTimer(timer);
- return frames/s;
- }
-
- double GDMovieSize(GDHandle device,int quickly)
- {
- return GDMovieRate(device,quickly)/GDFrameRate(device);
- }
-
- double GDMovieRate(GDHandle device,int quickly)
- {
- Timer *timer;
- register long image;
- long images;
- OSErr error;
- double s=NAN,fractionOfFrame=NAN;
- PixMap **pm;
- unsigned long bytes;
- GDHandle oldDevice;
- WindowPtr window,oldPort;
- static Rect r,rLocal;
- Ptr oldBaseAddr;
- short mode;
- short nominalBits,trueBits;
- Handle saveSpace,bufferHandle;
- long osAttr;
- int tempMem;
-
- if(!QD8Exists())return GDMovieRateNoColorQuickDraw(quickly);
- oldDevice=GetGDevice();
- SetGDevice(device);
- pm=NewPixMap();
- SetGDevice(oldDevice);
- if(pm==NULL)goto done0;
- HLock((Handle)pm);
- // The color table is needed for CopyBits(); CopyBitsQuickly doesn't care.
- (**pm).pmTable=(**(**device).gdPMap).pmTable; // share device's color table
- if(device==GetMainDevice())(**pm).bounds.top+=MBarHeight;
- if(SHOW_MOVIE_WINDOW){
- (**pm).bounds.top+=19; // Allow room for window title
- InsetRect(&(**pm).bounds,32,32);
- }
- bufferHandle=NULL;
- bytes=(**pm).bounds.right-(**pm).bounds.left;
- bytes*=(**pm).pixelSize;
- bytes=((bytes+31)/32)*4; // convert bits to bytes, rounding up to multiple of 4
- (**pm).rowBytes &= ~0x3fff;
- (**pm).rowBytes |= bytes;
- Gestalt(gestaltOSAttr,&osAttr);
- tempMem=osAttr & 1L<<gestaltTempMemSupport;
- while(1){
- bytes=(**pm).rowBytes & 0x3fff;
- bytes*=(**pm).bounds.bottom-(**pm).bounds.top;
- if(bytes==0)goto done1;
- saveSpace=NewHandle(2000); // save some space
- bufferHandle=NewHandle(bytes+1200); // extra is for drifting
- if(saveSpace!=NULL)DisposeHandle(saveSpace);
- if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+1200,&error);
- if(bufferHandle!=NULL)break;
- // Halve the window's height before trying again
- (**pm).bounds.bottom=(**pm).bounds.top+((**pm).bounds.bottom-(**pm).bounds.top)/2;
- }
- HLock(bufferHandle);
- (**pm).baseAddr=*bufferHandle;
- GetPort(&oldPort);
- r=(**pm).bounds;
- window=NewCWindow(NULL,&r,"\pmovie",0,0,(WindowPtr)-1,0,0); // don't show it yet
- if(window==NULL)goto done2;
- timer=NewTimer();
- if(timer==NULL)goto done2; // lacks Revised Time Manager.
- SetPort(window);
- HLock((Handle)((CWindowPtr)window)->portPixMap);
- SetGDevice(device);
- rLocal=r;
- GlobalToLocalRect(&rLocal);
- StartTimer(timer);
- if(quickly)CopyBitsQuickly((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
- ,&rLocal,&r,srcCopy,NULL); // copy screen to memory
- else{
- CopyBits((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
- ,&rLocal,&r,srcCopy,NULL); // copy screen to memory
- if(error=QDError()){
- printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
- goto done3;
- }
- }
- s=StopTimerSecs(timer); // rough estimate of time for one image
- if(SHOW_MOVIE_WINDOW || !quickly)ShowWindow(window);// CopyBits won't copy to a hidden window
- StartTimer(timer);
- oldBaseAddr=(**pm).baseAddr;
- // Let's time for this many seconds.
- images=ceil(1.0/s);
- for(image=images;image>0;image--){
- if(image==1)(**pm).baseAddr=oldBaseAddr;
- else (**pm).baseAddr+=4; // Drift image to prove it's a movie
- // We drift by multiples of 4 bytes because long-aligned copying is faster.
- if(quickly)CopyBitsQuickly((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
- ,&r,&rLocal,srcCopy,NULL); // copy memory to screen
- else CopyBits((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
- ,&r,&rLocal,srcCopy,NULL); // copy memory to screen
- }
- s=StopTimerSecs(timer);
- fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
- r=(**(**device).gdPMap).bounds;
- fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
- if(1){
- // Ask the video driver what mode we're in. In case QuickDraw's been fooled.
- GDGetMode(device,&mode,NULL,NULL);
- nominalBits=(**(**device).gdPMap).pixelSize;
- trueBits=1<<(mode&7);
- fractionOfFrame*=(double)nominalBits/trueBits;
- }
- DisposeTimer(timer);
- done3:
- SetPort(oldPort);
- DisposeWindow(window);
- done2:
- DisposHandle(bufferHandle);
- done1:
- (**pm).pmTable=NULL;
- DisposePixMap(pm);
- done0:
- SetGDevice(oldDevice);
- return images*fractionOfFrame/s;
- }
-
- double GDMovieRateNoColorQuickDraw(int quickly)
- {
- Timer *timer;
- register long image;
- long images;
- OSErr error=0;
- double s=NAN,fractionOfFrame=NAN;
- BitMap bitmap;
- unsigned long bytes;
- Rect r;
- Ptr oldBaseAddr;
- Handle saveSpace,bufferHandle=NULL;
- long osAttr;
- int tempMem;
- char string[100];
- GrafPort portRec,*port=&portRec,*oldPort;
-
- GetPort(&oldPort);
- OpenPort(port);
- SetPort(port);
- bitmap=port->portBits;
- bytes=bitmap.bounds.right-bitmap.bounds.left;
- bitmap.rowBytes=((bytes+31)/32)*4; // convert bits to bytes, round up to mult of 4
- Gestalt(gestaltOSAttr,&osAttr);
- tempMem=osAttr & 1L<<gestaltTempMemSupport;
- while(1){
- bytes=bitmap.rowBytes & 0x3fff;
- bytes*=bitmap.bounds.bottom-bitmap.bounds.top;
- if(bytes==0)goto done;
- saveSpace=NewHandle(2000); // save some space
- bufferHandle=NewHandle(bytes+600); // extra is for drifting
- if(saveSpace!=NULL)DisposeHandle(saveSpace);
- if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+600,&error);
- if(bufferHandle!=NULL)break;
- // Halve the window's height before trying again
- bitmap.bounds.bottom=bitmap.bounds.top+(bitmap.bounds.bottom-bitmap.bounds.top)/2;
- }
- HLock(bufferHandle);
- bitmap.baseAddr=*bufferHandle;
- timer=NewTimer();
- if(timer==NULL)goto done; // lacks Revised Time Manager.
- r=bitmap.bounds;
- StartTimer(timer);
- if(quickly)CopyBitsQuickly(&port->portBits,&bitmap
- ,&r,&r,srcCopy,NULL); // copy screen to memory
- else CopyBits(&port->portBits,&bitmap
- ,&r,&r,srcCopy,NULL); // copy screen to memory
- s=StopTimerSecs(timer); // approximate time for one image
- if(!quickly && (error=QDError())){
- printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
- DisposeTimer(timer);
- goto done;
- }
- oldBaseAddr=bitmap.baseAddr;
- images=ceil(1.0/s); // Let's time for this many seconds.
- StartTimer(timer);
- for(image=images;image>0;image--){
- if(image==1)bitmap.baseAddr=oldBaseAddr;
- else bitmap.baseAddr+=2; // Drift the image, to prove it's a movie
- if(quickly)CopyBitsQuickly(&bitmap,&port->portBits
- ,&r,&r,srcCopy,NULL); // copy memory to screen
- else CopyBits(&bitmap,&port->portBits
- ,&r,&r,srcCopy,NULL); // copy memory to screen
- }
- s=StopTimerSecs(timer);
- DisposeTimer(timer);
- if(!quickly && (error=QDError())){
- printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
- DisposeTimer(timer);
- goto done;
- }
- fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
- r=port->portBits.bounds;
- fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
- done:
- SetPort(oldPort);
- ClosePort(port);
- if(bufferHandle==NULL){
- printf("GDMovieRate: Not enough memory!\n");
- return NAN;
- }
- DisposeHandle(bufferHandle);
- return images*fractionOfFrame/s;
- }
-
- double TickRate(void)
- {
- Timer *timer;
- double s;
- long t;
-
- timer=NewTimer();
- if(timer==NULL)return NAN; // lacks Revised Time Manager.
- Delay(1,&t);
- StartTimer(timer);
- Delay(FRAMES,&t);
- s=StopTimerSecs(timer);
- DisposeTimer(timer);
- return FRAMES/s;
- }
-
- double GDFramesPerClutUpdate(GDHandle device,long clutEntries)
- // OBSOLETE. Use GDTimeClut() instead.
- {
- double frames;
-
- GDTimeClut(device,GDSetEntries,clutEntries,NULL,&frames,NULL,NULL);
- return frames;
- }
-
- double GDClutUpdateRate(GDHandle device,long clutEntries)
- // OBSOLETE. Use GDTimeClut() instead.
- {
- double s;
-
- GDTimeClut(device,GDSetEntries,clutEntries,&s,NULL,NULL,NULL);
- return 1.0/s;
- }
-
- void GDTimeClutUpdate(GDHandle device,long clutEntries,double *framesPtr,double *sPtr)
- // OBSOLETE. Use GDTimeClut() instead.
- {
- double missingFrames;
-
- GDTimeClut(device,GDSetEntries,clutEntries,sPtr,framesPtr,&missingFrames,NULL);
-
- // We're trying replicate the old behavior, so remove the correction.
- if(framesPtr!=NULL)*framesPtr-=missingFrames;
- }
-
-